//
//  AutomaticReferenceCounting .swift
//  cnswift-demo
//
//  Created by wyang on 2022/8/25.
//  Copyright © 2022 YangWei. All rights reserved.
//

import Foundation

class AutomaticReferenceCounting : SWDBaseObject {
    
    class func run() {
        
        let automaticReferenceCounting = AutomaticReferenceCounting()
        
        // 2 自动引用计数实践
        automaticReferenceCounting.ARCInAction()
        
        // 3 类实例之间的循环强引用
        automaticReferenceCounting.strongReferenceCyclesBetweenClassInstances()
        
        // 4.1 弱引用
        automaticReferenceCounting.weakReferences()
        
        // 4.2 无主引用
        automaticReferenceCounting.unownedReferences()
        
        // 4.3 无主可选引用
        automaticReferenceCounting.unownedOptionalReferences()
        
        // 4.4 无主引用和隐式解包可选值属性
        automaticReferenceCounting.unownedReferencesAndImplicitlyUnwrappedOptionalProperties()
        
        // 5 闭包的循环强引用
        automaticReferenceCounting.strongReferenceCyclesForClosures()
        
        // 6.2 弱引用和无主引用
        automaticReferenceCounting.weakAndUnownedReferences()
    }
    
    /// 2 自动引用计数实践
    func ARCInAction() {
        let title = "2 自动引用计数实践";
        printStartLine(title)
        
        // 可选类型，值会被自动初始化为 nil
        var reference1: Person?
        var reference2: Person?
        var reference3: Person?

        // 创建 Person 类的新实例，赋值给三个变量中的一个：
        reference1 = Person(name: "John Appleseed")
        // John Appleseed 正在被构造初始化
        
        // 将同一个 Person 实例也赋值给其他两个变量，该实例又会多出两个强引用：
        reference2 = reference1
        reference3 = reference1
        
        reference1 = nil
        reference2 = nil

        // 最后一个强引用被断开时，ARC 会销毁它：
        reference3 = nil
        // 打印“John Appleseed is being deinitialized”
        
        /// 2 自动引用计数实践
        /// 人
        class Person {
            let name: String
            // 构造器
            init(name: String) {
                self.name = name
                print("\(name) 正在被构造初始化")
            }
            // 析构器
            deinit {
                print("\(name) 正在被分解")
            }
        }
        
        printEndLine(title)
    }
    
    /// 3 类实例之间的循环强引用
    func strongReferenceCyclesBetweenClassInstances() {
        let title = "3 类实例之间的循环强引用";
        printStartLine(title)
        
        /// 人
        class Person {
            let name: String
            init(name: String) { self.name = name }
            // 属性可选，一个人并不总是拥有公寓，默认初始化为 nil
            var apartment: Apartment?
            deinit { print("\(name) 正在被分解销毁") }
        }
        
        /// 公寓
        class Apartment {
            let unit: String
            init(unit: String) { self.unit = unit }
            // 属性可选，一栋公寓并不总是有居民，默认初始化为 nil
            var tenant: Person?
            deinit { print("公寓 \(unit) 正在被分解销毁") }
        }
        
        // 定义两个可选类型变量 john 和 unit4A（默认初始化为 nil）
        var john: Person?
        var unit4A: Apartment?

        john = Person(name: "John Appleseed")
        unit4A = Apartment(unit: "4A")
        
        // 感叹号用来解包和访问可选变量 john 和 unit4A 中的实例
        john!.apartment = unit4A
        unit4A!.tenant = john
        
        john = nil
        unit4A = nil
        // 没有打印分解销毁
        
        printEndLine(title)
    }
    
    /// 4.1 弱引用
    func weakReferences() {
        let title = "4.1 弱引用";
        printStartLine(title)
        
        /// 人：同 3 类实例之间的循环强引用 无变化
        class Person {
            let name: String
            init(name: String) { self.name = name }
            // 属性可选，一个人并不总是拥有公寓，默认初始化为 nil
            var apartment: Apartment?
            deinit { print("\(name) 正在被分解销毁") }
        }
        
        /// tenant 属性被声明为弱引用
        class Apartment {
            let unit: String
            init(unit: String) { self.unit = unit }
            weak var tenant: Person?
            deinit { print("公寓 \(unit) 正在被分解销毁") }
        }
        
        // 建立两个变量（john 和 unit4A）之间的强引用，并关联两个实例：
        var john: Person?
        var unit4A: Apartment?
        john = Person(name: "John Appleseed")
        unit4A = Apartment(unit: "4A")
        john!.apartment = unit4A
        unit4A!.tenant = john
        
        john = nil
        // John Appleseed 正在被分解销毁
        
        unit4A = nil
        // 公寓 4A 正在被分解销毁
        
        printEndLine(title)
    }
    
    // TODO: 如何引用定义在函数里的嵌套类型 🤔️
    /*
     如：
     1）外面的 class 引用 里面函数作用域定义的 class 类型 ？
     2）扩展 函数作用域定义的 class 类型 ？
     */
    class TestYinyong {
//        var apartment: AutomaticReferenceCounting.Apartment?
    }
    
    /// 4.2 无主引用
    func unownedReferences() {
        let title = "4.2 无主引用";
        printStartLine(title)
        
        // 定义一个 john 的可选类型 Customer 变量（被初始化为 nil），保存某个特定客户的引用：
        var john: Customer?
        
        john = Customer(name: "John Appleseed")
        john!.card = CreditCard(number: 1234_5678_9012_3456, customer: john!)
        
        john = nil
        // 打印“John Appleseed is being deinitialized”
        // 打印“Card #1234567890123456 is being deinitialized”
        
        printEndLine(title)
    }
    
    /// 银行客户
    class Customer {
        let name: String
        // 将 CreditCard 实例作为自身的一个属性
        var card: CreditCard?
        init(name: String) {
            self.name = name
        }
        deinit { print("\(name) 实例对象正在被销毁") }
    }
    
    /// 客户的信用卡
    class CreditCard {
        let number: UInt64
        // 将 Customer 实例作为自身的一个属性：定义为无主引用，避免循环强引用
        unowned let customer: Customer
        init(number: UInt64, customer: Customer) {
            self.number = number
            self.customer = customer
        }
        deinit { print("信用卡 #\(number) 实例对象正在被销毁") }
    }
    
    /// 4.3 无主可选引用
    func unownedOptionalReferences() {
        let title = "4.3 无主可选引用";
        printStartLine(title)
        
        // 创建系科和它的三个课程
        let department = Department(name: "园艺学（Horticulture）")
        let intro = Course(name: "植物调查（Survey of Plants）", in: department)
        let intermediate = Course(name: "种植普通草本植物（Growing Common Herbs）", in: department)
        let advanced = Course(name: "照顾热带植物（Caring for Tropical Plants）", in: department)
        intro.nextCourse = intermediate
        intermediate.nextCourse = advanced
        department.courses = [intro, intermediate, advanced]
        
        printEndLine(title)
    }
    
    /// 学校特定系科
    class Department {
        var name: String
        // 维持着对其提供的每个课程的强引用
        var courses: [Course]
        init(name: String) {
            self.name = name
            self.courses = []
        }
    }

    /// 课程
    class Course {
        var name: String
        // 无主引用，它对应的系科
        unowned var department: Department
        // 无主引用，学生应该修的后续课程，某些课程没有推荐的后续课程，所以属性是可选的
        unowned var nextCourse: Course?
        init(name: String, in department: Department) {
            self.name = name
            self.department = department
            self.nextCourse = nil
        }
    }
    
    /// 4.4 无主引用和隐式解包可选值属性
    func unownedReferencesAndImplicitlyUnwrappedOptionalProperties() {
        let title = "4.4 无主引用和隐式解包可选值属性";
        printStartLine(title)
        
        let country = Country(name: "加拿大（Canada）", capitalName: "温哥华（Ottawa）")
        print("\(country.name) 的首都叫 \(country.capitalCity.name)")
        // 加拿大（Canada） 的首都叫 温哥华（Ottawa）
        
        printEndLine(title)
    }
    
    /// 国家
    class Country {
        let name: String
        // 每个国家必须有首都
        var capitalCity: City!
        // 构造器调用了 City 的构造器
        init(name: String, capitalName: String) {
            self.name = name
            // 只有 Country 实例完全初始化后，Country 构造器才能把 self 传给 City 构造器
            self.capitalCity = City(name: capitalName, country: self)
        }
    }
    
    /// 城市
    class City {
        let name: String
        // 每个城市必须属于一个国家
        unowned let country: Country
        // 构造器接受一个 Country 实例作为参数，并且将实例保存到 country 属性
        init(name: String, country: Country) {
            self.name = name
            self.country = country
        }
    }
    
    /// 5 闭包的循环强引用
    func strongReferenceCyclesForClosures() {
        let title = "5 闭包的循环强引用";
        printStartLine(title)
        
        // 用自定义的闭包来取代默认值
        let heading = HTMLElement(name: "h1")
        let defaultText = "some default text"
        heading.asHTML = {
            return "<\(heading.name)>\(heading.text ?? defaultText)</\(heading.name)>"
        }
        print(heading.asHTML())
        // <h1>some default text</h1>
        
        
        // 使用 HTMLElement 类创建实例并打印消息：
        var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
        print(paragraph!.asHTML())
        // <p>hello, world</p>
        
        paragraph = nil
        // HTMLElement 的析构器中的消息并没有被打印，证明了 HTMLElement 实例并没有被销毁
        
        printEndLine(title)
    }
    
    /// 模拟 HTML 元素
    class HTMLElement {
        // 这个元素的名称，代表头部元素的 "h1"，代表段落的 "p"，或者代表换行的 "br"
        let name: String
        // 可选属性 text，用来设置 HTML 元素呈现的文本
        let text: String?
        // 属性引用一个将 name 和 text 组合成 HTML 字符串片段的闭包
        // 该属性是 Void -> String 类型，可以理解为“一个没有参数，返回 String 的函数”
        lazy var asHTML: () -> String = {
            if let text = self.text {
                return "<\(self.name)>\(text)</\(self.name)>"
            } else {
                return "<\(self.name) />"
            }
        }
        
        // 通过 name 和 text（如果有的话）参数来初始化一个新元素
        init(name: String, text: String? = nil) {
            self.name = name
            self.text = text
        }
        
        deinit {
            // 当 HTMLElement 实例被销毁时，打印一条消息
            print("\(name) 正在被销毁")
        }
    }
    
    /// 6.2 弱引用和无主引用
    func weakAndUnownedReferences() {
        let title = "6.2 弱引用和无主引用";
        printStartLine(title)
        
        class HTMLElement {
            let name: String
            let text: String?
            lazy var asHTML: () -> String = {
                // 捕获列表是 [unowned self]，表示“将 self 捕获为无主引用而不是强引用”
                [unowned self] in
                if let text = self.text {
                    return "<\(self.name)>\(text)</\(self.name)>"
                } else {
                    return "<\(self.name) />"
                }
            }
            init(name: String, text: String? = nil) {
                self.name = name
                self.text = text
            }
            deinit {
                print("\(name) 正在被销毁")
            }
        }
        
        
        // 使用 HTMLElement 类创建实例并打印消息：
        var paragraph: HTMLElement? = HTMLElement(name: "p", text: "hello, world")
        print(paragraph!.asHTML())
        // <p>hello, world</p>
        
        // HTMLElement 实例将被销毁，并能看到它的析构器打印出消息
        paragraph = nil
        // p 正在被销毁
        
        printEndLine(title)
    }
}
